package com.dongbao.service;

import com.dongbao.common.AwardedInfoConstant;
import com.dongbao.core.rules.*;
import com.dongbao.core.rules.java.*;
import com.dongbao.core.rules.xml.SqlIsDeleteRules;
import com.dongbao.core.rules.xml.SqlJoinTableRules;
import com.dongbao.entity.AwardedInfo;
import com.dongbao.entity.CodeScoreInfo;
import com.dongbao.util.CodeInspectionUtil;
import com.intellij.ide.highlighter.JavaFileType;
import com.intellij.ide.highlighter.XmlFileType;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiFile;
import lombok.extern.java.Log;
import org.dom4j.*;

import java.lang.reflect.InvocationTargetException;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;


/**
 * 代码分数计算 服务
 * */
@Log
public class CodeGradeBiz {

    private Map<String,CodeRulesBase> javaCodeRules;

    private Map<String,CodeRulesBase> xmlRules;

    public void initRules(VirtualFile afterFile,PsiFile psiFile,Project project){
        /* 初始化代码规则验证 */
        if(javaCodeRules == null){
            javaCodeRules = new HashMap<String,CodeRulesBase>();
        }
        if(xmlRules == null){
            xmlRules = new HashMap<String,CodeRulesBase>();
        }
        //检测方法的注解是否合格,对应代码检查规范中【代码结构】-【方法结构要体现主逻辑清晰（列点，体现核心思路）】
        javaCodeRules.put("AnnotationsCodeRules",new AnnotationsCodeRules(afterFile,psiFile,project));
        //遵循单一职责（一个方法只做一件事)，封装数据返回结果情况可不抽
        javaCodeRules.put("DuplicatedCodeRules",new DuplicatedCodeRules(afterFile,psiFile,project));
        //方法参数最多不能超过5个，超过考虑用对象，方法体内共用变量超过6个,必须封装成Dto或者简化入参参数
        javaCodeRules.put("MethodParamRules",new MethodParamRules(afterFile,psiFile,project));
        //变量符合小驼峰命名，达到名字即注释的效果
        javaCodeRules.put("SmallHumpRules",new SmallHumpRules(afterFile,psiFile,project));
        //巧用建造者模式进行对象组合创建（不同的组合生成出所谓的套餐)
        javaCodeRules.put("StructurePatternRules",new StructurePatternRules(afterFile,psiFile,project));
        //数据库表名及字段命名符合小驼峰命名，达到名字即注释的效果
        javaCodeRules.put("DataBaseTableNameRules",new DataBaseTableNameRules(afterFile,psiFile,project));
        //必须使用了多语言设置
        javaCodeRules.put("MultilingualRules",new MultilingualRules(afterFile,psiFile,project));
        //for循环体内,循环调用增删改查数据库或查询编号, 提取for外层, 可采用批量查询方式进行一次性集合查询，大大提高性能.
        javaCodeRules.put("ForDBOperationRules",new ForDBOperationRules(afterFile,psiFile,project));
        //函数式的变量定义是否添加注释,表明意图
        javaCodeRules.put("FunctionVariableDefineRules",new FunctionVariableDefineRules(afterFile,psiFile,project));
        //禁止出现魔法值
//        codeRules.put("MagicPointRules",new MagicPointRules(afterFile));
        //方法体内的代码行数建议不超过120行? 超过建议召开会议
        javaCodeRules.put("CodeLineNumberMaxRules",new CodeLineNumberMaxRules(afterFile,psiFile,project));
        //方法体内的代码行数建议不超过120行? 超过建议召开会议
        javaCodeRules.put("MapOperationRules",new MapOperationRules(afterFile,psiFile,project));
        //方法(做两件及以上事情)的头部有注释,注释需体现整个方法的思路（最好列点说明） 只做一件事情的方法,写好方法描述.
        javaCodeRules.put("MethodCommenRules",new MethodCommenRules(afterFile,psiFile,project));
        //同一个service注入了多次同一个DAO,Controller同理(刘弦)
        javaCodeRules.put("RepeatImportRules",new RepeatImportRules(afterFile,psiFile,project));
        // 在一个事务中不能同时对同一张表做查询新增修改 删除，只要存在两种的就算，只提示，不扣分
        javaCodeRules.put("LockTableRules",new LockTableRules(afterFile,psiFile,project));

        //Sql不允许关联三张以上的表,将会影响查询效率
        xmlRules.put("SqlJoinTableRules",new SqlJoinTableRules(afterFile,psiFile,project));
        //Sql不允许关联三张以上的表,将会影响查询效率
        xmlRules.put("SqlIsDeleteRules",new SqlIsDeleteRules(afterFile,psiFile,project));


    }

    /**
     * 计算一个文件的 加减分情况,并记录得分
     * */
    public CodeScoreInfo calculate(VirtualFile afterFile,PsiFile psiFile, CodeScoreInfo codeScoreInfo,Project project){
        /* 初始化规则 */
        initRules(afterFile,psiFile,project);
        List<AwardedInfo> deduction = codeScoreInfo.getDeduction();
        if((afterFile.getFileType() instanceof JavaFileType)){
            try {
                /* TODO:使用PMD方式去分析提交代码 */
                pmd(afterFile,deduction);
                /* TODO:使用IDEA方式去分析提交代码 */
                idea(afterFile,deduction);
            } catch (Exception e) {
                e.printStackTrace();
            }
            //使用问题编号排个序
            deduction = deduction.stream().sorted(Comparator.comparing(AwardedInfo::getCode)).collect(Collectors.toList());
            return codeScoreInfo;
        }else if((afterFile.getFileType() instanceof XmlFileType) && afterFile.getName().contains("Dao.xml")){
            /* TODO:分析XML文件是否满足规则 */
            xml(afterFile,deduction);
        }
        //使用问题编号排个序
        deduction = deduction.stream().sorted(Comparator.comparing(AwardedInfo::getCode)).collect(Collectors.toList());
        return null;
    }

    /**
     * xml类型文件解析规范
     * */
    public void xml(VirtualFile afterFile,List<AwardedInfo> deduction){/* 初始化规则 */

        for(String name:xmlRules.keySet()){
            long startTime = System.currentTimeMillis();
            System.out.println("正在执行代码规则:" + name);
            CodeRulesBase codeRule = xmlRules.get(name);
            codeRule.run(deduction);
            long sendTime = System.currentTimeMillis();
        }
    }

    /**
     * 使用IDEA代码分析方式去解析语法规范
     * */
    public void idea(VirtualFile afterFile,List<AwardedInfo> deduction) throws InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
        for(String name:javaCodeRules.keySet()){
            long startTime = System.currentTimeMillis();
            System.out.println("正在执行代码规则:" + name);
            CodeRulesBase codeRule = javaCodeRules.get(name);
            codeRule.run(deduction);
            long sendTime = System.currentTimeMillis();
        }
    }

    /**
     * 使用PMD方式分析代码
     * */
    public void pmd(VirtualFile afterFile,List<AwardedInfo> deduction) throws DocumentException {
        /* 执行分析代码 */
        String analyseResult = CodeInspectionUtil.checkCode(afterFile.getPath());
        Document document = DocumentHelper.parseText(analyseResult);
        Element rootElement = document.getRootElement();
        Element childElement = rootElement.element("file");
        if(childElement == null){
            return ;
        }
        for (int i = 0; i < childElement.attributeCount(); i++) {
            Attribute name = childElement.attribute(i);
            String names = name.getName();
            String value =  childElement.attributeValue(names);
            /* 获取到当前检测的文件名 */
            String javaFile = name.getName();
            /* 遍历子节点获取检测数据 */
            List<Element> violations = childElement.elements("violation");
            /* 遍历获取节点属性 */
            for(Element violation:violations){
                AwardedInfo awardedInfo=new AwardedInfo();
                awardedInfo.setVirtualFile(afterFile);
                awardedInfo.setFileName(afterFile.getName());
                for(int j=0;j<violation.attributeCount();j++){
                    Attribute violationAttr = violation.attribute(j);
                    String violationAttrName = violationAttr.getName();
                    String violationAttrValue = violationAttr.getValue();
//                    System.out.println("violation====="+violationAttrName+":"+violationAttrValue+"  ");
                    switch (violationAttrName){
                        case "beginline":
                            awardedInfo.setBeginline(Integer.valueOf(violationAttrValue));
                            break;
                        case "endline":
                            awardedInfo.setEndline(Integer.valueOf(violationAttrValue));
                            break;
                        case "begincolumn":
                            awardedInfo.setBegincolumn(Integer.valueOf(violationAttrValue));
                            break;
                        case "endcolumn":
                            awardedInfo.setEndcolumn(Integer.valueOf(violationAttrValue));
                            break;
                        case "rule":
                            awardedInfo.setRule(violationAttrValue);
                            break;
                        case "ruleset":
                            awardedInfo.setRuleset(violationAttrValue);
                            break;
                        case "package":
                            awardedInfo.setJavaPackage(violationAttrValue);
                            break;
                        case "class":
                            awardedInfo.setJavaClass(violationAttrValue);
                            break;
                        case "method":
                            awardedInfo.setJavaMethod(violationAttrValue);
                            break;
                        case "externalInfoUrl":
                            awardedInfo.setExternalInfoUrl(violationAttrValue);
                            break;
                        case "priority":
                            awardedInfo.setPriority(violationAttrValue);
                            break;
                    }
                }
                /* 获取检测内容信息 */
                String text = violation.getText();
                if(text.indexOf("魔法值")!=-1){
                    System.out.println("666");
                    text = "A A009 " + text;
                }
                if(text.indexOf("A ")!=-1 || text.indexOf("B ")!=-1 || text.indexOf("C ")!=-1 || text.indexOf("D ")!=-1){
                    String[] desc = text.split(" ");
                    awardedInfo.setLevel(desc[0]);
                    awardedInfo.setCode(desc[1]);
                    awardedInfo.setDescribe(desc[2]);
                }else{
                    awardedInfo.setDescribe(text);
                }
                /* 根据检测出来的问题类型来控制加减分数 */
                awardedInfo.setScore(2);
                awardedInfo.setType(AwardedInfoConstant.PMD);
                /* 匹配响应的Code给规则 */


                deduction.add(awardedInfo);
            }
        }
    }

    /**
     * 最终评分分数计算
     * */
    public Integer score(List<AwardedInfo> deductions,CodeScoreInfo codeScoreInfo){
        if(deductions.isEmpty()){
            codeScoreInfo.setScore(100);
            return 100;
        }
        Integer allScore = codeScoreInfo.getScore();
        for(AwardedInfo deduction:deductions){
            allScore = allScore - deduction.getScore();
        }
        codeScoreInfo.setScore(allScore);
        return allScore;
    }


}
